/*
 * Copyright 2007 - 2025 Ralf Wisser.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.sf.jailer.ui.databrowser;

import java.awt.GridBagConstraints;
import java.awt.Point;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import org.fife.ui.rtextarea.RTextScrollPane;

import net.sf.jailer.ExecutionContext;
import net.sf.jailer.database.Session;
import net.sf.jailer.ui.Colors;
import net.sf.jailer.ui.Environment;
import net.sf.jailer.ui.UIUtil;
import net.sf.jailer.ui.databrowser.metadata.MetaDataSource;
import net.sf.jailer.ui.databrowser.sqlconsole.MetaDataBasedSQLCompletionProvider;
import net.sf.jailer.ui.databrowser.sqlconsole.SQLConsole;
import net.sf.jailer.ui.syntaxtextarea.RSyntaxTextAreaWithSQLSyntaxStyle;
import net.sf.jailer.ui.syntaxtextarea.RSyntaxTextAreaWithTheme;
import net.sf.jailer.ui.syntaxtextarea.SQLAutoCompletion;
import net.sf.jailer.util.SqlScriptExecutor;

/**
 * Editor for SQL/DML statements.
 *
 * @author Ralf Wisser
 */
public class SQLDMLPanel extends javax.swing.JPanel {

	private static final long serialVersionUID = 1747749941444843829L;

	/**
	 * The DB session.
	 */
	private final Session session;

	/**
	 * To be done after execution of the script.
	 */
	private final Runnable afterExecution;
	private final Runnable switchToConsole;

	private final ExecutionContext executionContext;
	private final SQLConsole sqlConsole;
	private final JDialog dialog;

	/** Creates new form SQLPanel
	 * @param sql
	 * @param metaDataSource
	 * @param dialog */
	@SuppressWarnings("serial")
	public SQLDMLPanel(String sql, SQLConsole sqlConsole, Session session, MetaDataSource metaDataSource, Runnable afterExecution, Runnable switchToConsole, JDialog dialog, ExecutionContext executionContext) {
		this.executionContext = executionContext;
		this.sqlConsole = sqlConsole;
		this.switchToConsole = switchToConsole;
		this.session = session;
		this.afterExecution = afterExecution;
		this.dialog = dialog;
		initComponents(); UIUtil.initComponents(this);
		
		closeButton.setIcon(closeIcon);
		executeButton.setIcon(runIcon);
		sqlConsoleButton.setIcon(runAllIcon);
		clipboardButton.setIcon(copyIcon);

		this.sqlTextArea = new RSyntaxTextAreaWithSQLSyntaxStyle(false, false) {
			@Override
			protected void runBlock() {
				super.runBlock();
				executeButtonActionPerformed(null);
			}
		};
		try {
			MetaDataBasedSQLCompletionProvider provider = new MetaDataBasedSQLCompletionProvider(session, metaDataSource);
			new SQLAutoCompletion(provider, sqlTextArea);
		} catch (SQLException e) {
		}

		scrollPane = new RTextScrollPane();
		scrollPane.setViewportView(sqlTextArea);

		GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints();
		gridBagConstraints.gridx = 1;
		gridBagConstraints.gridy = 1;
		gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
		gridBagConstraints.weightx = 1.0;
		gridBagConstraints.weighty = 1.0;
		jPanel1.add(scrollPane, gridBagConstraints);

		scrollPane.setLineNumbersEnabled(true);

		statusLabel.setText("");
		sqlTextArea.setText(sql);
		sqlTextArea.select(0, 0);

		UIUtil.invokeLater(new Runnable() {
			@Override
			public void run() {
				sqlConsoleButton.grabFocus();
			}
		});
	}

	/** This method is called from within the constructor to
	 * initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is
	 * always regenerated by the Form Editor.
	 */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        clipboardSingleLineButton = new javax.swing.JButton();
        jPanel2 = new javax.swing.JPanel();
        saveButton = new javax.swing.JButton();
        clipboardButton = new javax.swing.JButton();
        executeButton = new javax.swing.JButton();
        singleLineCheckBox = new javax.swing.JCheckBox();
        statusLabel = new javax.swing.JLabel();
        jPanel4 = new javax.swing.JPanel();
        sqlConsoleButton = new javax.swing.JButton();
        closeButton = new javax.swing.JButton();
        jPanel1 = new javax.swing.JPanel();
        jPanel3 = new javax.swing.JPanel();

        clipboardSingleLineButton.setText(" Copy as Single Line ");
        clipboardSingleLineButton.setToolTipText(" Copy the SQL statement as a single line to the clipboard");
        clipboardSingleLineButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                clipboardSingleLineButtonActionPerformed(evt);
            }
        });

        setLayout(new java.awt.GridBagLayout());

        jPanel2.setLayout(new java.awt.GridBagLayout());

        saveButton.setText(" Save ");
        saveButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
        jPanel2.add(saveButton, gridBagConstraints);

        clipboardButton.setText(" Copy to Clipboard ");
        clipboardButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                clipboardButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
        jPanel2.add(clipboardButton, gridBagConstraints);

        executeButton.setText(" Execute ");
        executeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                executeButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 4;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
        jPanel2.add(executeButton, gridBagConstraints);

        singleLineCheckBox.setText("single line  ");
        singleLineCheckBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                singleLineCheckBoxItemStateChanged(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.insets = new java.awt.Insets(0, 12, 0, 0);
        jPanel2.add(singleLineCheckBox, gridBagConstraints);

        statusLabel.setText("jLabel1");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 8, 0, 0);
        jPanel2.add(statusLabel, gridBagConstraints);

        jPanel4.setLayout(new java.awt.GridBagLayout());
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        jPanel2.add(jPanel4, gridBagConstraints);

        sqlConsoleButton.setText("SQL Console");
        sqlConsoleButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sqlConsoleButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
        jPanel2.add(sqlConsoleButton, gridBagConstraints);

        closeButton.setText("Close");
        closeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                closeButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 5;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 2);
        jPanel2.add(closeButton, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weightx = 1.0;
        add(jPanel2, gridBagConstraints);

        jPanel1.setLayout(new java.awt.GridBagLayout());

        jPanel3.setLayout(new java.awt.GridBagLayout());
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        jPanel1.add(jPanel3, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        add(jPanel1, gridBagConstraints);
    }// </editor-fold>//GEN-END:initComponents

	private void saveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveButtonActionPerformed
		String fn = UIUtil.choseFile(null, ".", "Save SQL Query", "", this, false, false);
		if (fn != null) {
			try {
				PrintWriter out = new PrintWriter(new FileWriter(fn));
				out.print(sqlTextArea.getText());
				out.close();
			} catch (Exception e) {
				UIUtil.showException(this, "Error saving query", e, session);
			}
		}
}//GEN-LAST:event_saveButtonActionPerformed

	private void clipboardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clipboardButtonActionPerformed
		sqlTextArea.selectAll();
		sqlTextArea.copy();
		sqlTextArea.select(0, 0);
}//GEN-LAST:event_clipboardButtonActionPerformed

	private void clipboardSingleLineButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clipboardSingleLineButtonActionPerformed
		String orig = sqlTextArea.getText();
		sqlTextArea.setText(orig.replaceAll(" *(\n|\r)+ *", " "));
		sqlTextArea.selectAll();
		sqlTextArea.copy();
		sqlTextArea.setText(orig);
		sqlTextArea.select(0, 0);
	}//GEN-LAST:event_clipboardSingleLineButtonActionPerformed

	private String lastMultiLineSQL = "";

	private void singleLineCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_singleLineCheckBoxItemStateChanged
		if (singleLineCheckBox.isSelected()) {
			String lf = System.getProperty("line.separator", "\n");
			lastMultiLineSQL = sqlTextArea.getText();
			String EOL = "EOL498503458430EOL";
			sqlTextArea.setText(lastMultiLineSQL.replaceAll(";(\n|\r)", EOL).replaceAll(" *(\n|\r)+ *", " ").replaceAll(EOL + " *", ";" + lf));
		} else {
			sqlTextArea.setText(lastMultiLineSQL);
		}
		sqlTextArea.setCaretPosition(0);
	}//GEN-LAST:event_singleLineCheckBoxItemStateChanged

	private void executeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_executeButtonActionPerformed
		doExecute();
	}//GEN-LAST:event_executeButtonActionPerformed
	
	public void setContent(String sql) {
		Point pos = scrollPane.getViewport().getViewPosition();
		UIUtil.invokeLater(() -> {
			sqlTextArea.setText(sql.trim());
			UIUtil.invokeLater(() -> {
				scrollPane.getViewport().setViewPosition(pos);
			});
		});
	}

	protected void doExecute() {
		if (!UIUtil.canRunJailer()) {
			return;
		}
		if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(this, "Execute Statements?", "Execute", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE)) {
			String sqlFile;
			try {
				String sqlFileBase = Environment.newFile("temp_" + System.currentTimeMillis()).getPath();
				for (int i = 0; ; ++i) {
					sqlFile = sqlFileBase + i + ".sql";
					if (!new File(sqlFile).exists()) {
						break;
					}
				}
				PrintWriter out = new PrintWriter(new File(sqlFile));
				out.println(sqlTextArea.getText());
				out.close();
			} catch (Exception e) {
				UIUtil.showException(this, "Error", e, session);
				return;
			}
			List<String> args = new ArrayList<String>();
			args.add("import");
			args.add(sqlFile);
			args.addAll(session.getCliArguments());
			args.add("-transactional");
			if (UIUtil.runJailer(SwingUtilities.getWindowAncestor(this), args, false, true,
			true, null, session.getSchema(), session.getPassword(), null, null, true,
			false, false, true, true, null, null, true, executionContext)) {
				statusLabel.setText("Executed " + SqlScriptExecutor.getLastStatementCount().a + " statements. " +
						SqlScriptExecutor.getLastStatementCount().b + " rows affected");
				statusLabel.setForeground(Colors.Color_0_100_0);
				afterExecution.run();
				// JOptionPane.showMessageDialog(this, "Successfully executed " + SqlScriptExecutor.getLastStatementCount().a + " statements.\n" + SqlScriptExecutor.getLastStatementCount().b + " rows affected.", "SQL/DML", JOptionPane.INFORMATION_MESSAGE);

			} else {
				statusLabel.setText("Error, rolled back");
				statusLabel.setForeground(Colors.Color_115_0_0);
			}
			new File(sqlFile).delete();
		}
	}

    private void sqlConsoleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sqlConsoleButtonActionPerformed
        switchToConsole.run();
    	UIUtil.invokeLater(8, () -> sqlConsole.appendStatement(sqlTextArea.getText(), false));
        dialog.dispose();
    }//GEN-LAST:event_sqlConsoleButtonActionPerformed

    private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
    	dialog.dispose();
    }//GEN-LAST:event_closeButtonActionPerformed


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton clipboardButton;
    private javax.swing.JButton clipboardSingleLineButton;
    protected javax.swing.JButton closeButton;
    private javax.swing.JButton executeButton;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    protected javax.swing.JButton saveButton;
    protected javax.swing.JCheckBox singleLineCheckBox;
    private javax.swing.JButton sqlConsoleButton;
    public javax.swing.JLabel statusLabel;
    // End of variables declaration//GEN-END:variables

    private final RSyntaxTextAreaWithTheme sqlTextArea;
    public RTextScrollPane scrollPane;

	private static ImageIcon closeIcon;
	private static ImageIcon runIcon;
	private static ImageIcon runAllIcon;
	private static ImageIcon copyIcon;
	static {
		// load images
		closeIcon = UIUtil.scaleIcon(new JLabel(""), UIUtil.readImage("/buttoncancel.png"));
		runIcon = UIUtil.scaleIcon(new JLabel(""), UIUtil.readImage("/run.png"));
		runAllIcon = UIUtil.scaleIcon(new JLabel(""), UIUtil.readImage("/runall.png"));
        copyIcon = UIUtil.scaleIcon(new JLabel(""), UIUtil.readImage("/copy.png"));
	}

}
